#pragma warning(disable:4996)

//#include "stdafx.h"
#include "keyHook.h"

//init static var
HHOOK	KeyHook::khook = NULL;
bool	KeyHook::hookissetted =false;
//bool	KeyHook::hook_flag = true;
keyMap	KeyHook::key_follow;
keyMap	KeyHook::key_instead;
keyFuncMap KeyHook::key_event_dwn;
keyFuncMap KeyHook::key_event_up;
mkeyFuncMap KeyHook::mkeyfuncmap;

//overload operator "<" of vector<string> so it can be a map key type
bool operator<(const vector<string>&lhs,const vector<string>&rhs)
{
	vector<string>::const_iterator it_vecl,it_vecr;
	for( it_vecl=lhs.begin(), it_vecr=rhs.begin();
		 it_vecl != lhs.end() && it_vecr != rhs.end();
		 it_vecl++, it_vecr++ )
	{
		if( *it_vecl < *it_vecr)
			return false;
	}
	return  true;
}

KeyHook::KeyHook()
{
	//do nothing
}

bool KeyHook::keyHookInit()
{
	if( hookissetted == false )
	{
		khook=SetWindowsHookEx( WH_KEYBOARD_LL, //lowlever keyhook 
			(HOOKPROC)keyHookProc,			    //keyhook procedure
			GetModuleHandle(NULL),
			0 );								//specilfy this hook is a globle hook effect to all application
		if( khook == NULL )                    //if NULL hook failed
		{
			char tmp[20];
			DWORD err=GetLastError();
			memset( tmp, 0x00, sizeof(tmp) );
			sprintf( tmp, "%ld", err );
			std::string errMsg(tmp);
			MessageBox( NULL, errMsg.c_str(), "set keyHook failed", MB_OK );
			return false;
		}
		hookissetted=true;
		DMsg( _T("set keyHook success"), "set keyhook" );
	}

	else
	{ DMsg( "HOOK already setted!", "set keyhook" ); }

	return true;
}

bool KeyHook::unHook()
{
	if( hookissetted == true )
	{
		UnhookWindowsHookEx( khook );
		hookissetted = false;
	}
	return !hookissetted;
}

LRESULT CALLBACK KeyHook::keyHookProc( int nCode, WPARAM wp, LPARAM lp )
{
	key ckey;   //current key that pressed
	KBDLLHOOKSTRUCT *lpt=(KBDLLHOOKSTRUCT*)lp;
	ckey=lpt->vkCode;

	if( DoHook() )
	{
		//event prosess of keydown
		if( wp == WM_KEYDOWN )
		{ 
			//keyInstead
			keyMap::iterator it_keyisd;
			it_keyisd = key_instead.find(ckey);
			if( it_keyisd != key_instead.end() )
			{
				keybd_event( it_keyisd->second, 0, 0, 0 );
				return true;										//return true to interrupt the keyevent
			}

			//key follow
			keyMap::iterator it_keyflw;
			it_keyflw = key_follow.find(ckey);
			if( it_keyflw != key_follow.end() )
			{
				keybd_event( it_keyflw->first, 0, 0, 0 );
				keybd_event( it_keyflw->second, 0, 0, 0 );
				return true;
			}

			//key down function
			keyFuncMap::iterator it_keyfuncmap;
			it_keyfuncmap = key_event_dwn.find( ckey );
			if( it_keyfuncmap != key_event_dwn.end() )
			{
				(*(it_keyfuncmap->second))(ckey);
				return true;
			}

			//multi key function
			mkeyFuncMap::iterator it_mkeyfuncmap;
			if( mkeyfuncmap.empty() )
			{ return true; }
			for( it_mkeyfuncmap = mkeyfuncmap.begin(); 
				it_mkeyfuncmap != mkeyfuncmap.end(); 
				it_mkeyfuncmap++ )
			{
				if( ChkMkey( it_mkeyfuncmap->first) )
				{
					(*(it_mkeyfuncmap->second))();
					return true;
				}
			}
		}

		//event prosess of keyup
		if( wp == WM_KEYUP )
		{ 
			//keyInstead
			keyMap::iterator it_keyisd;
			it_keyisd=key_instead.find(ckey);
			if( it_keyisd != key_instead.end() )
			{
				keybd_event( it_keyisd->second, 0, 2, 0 );
				return true;
			}

			//key follow
			keyMap::iterator it_keyflw;
			it_keyflw = key_follow.find(ckey);
			if( it_keyflw != key_follow.end() )
			{
				keybd_event( it_keyflw->first, 0, 2, 0 );
				keybd_event( it_keyflw->second, 0, 2, 0 );
				return true;
			}

			//key up function
			keyFuncMap::iterator it_keyFuncMap;
			it_keyFuncMap = key_event_up.find( ckey );
			if( it_keyFuncMap != key_event_up.end() )
			{
				(*(it_keyFuncMap->second))(ckey);
				return true;
			}
		}
	}

	return CallNextHookEx( khook, nCode, wp, lp);
}

bool KeyHook::keyInstead(key skey, key dkey)
{
	key_instead.insert( keyMap::value_type(skey,dkey) );
	return true;
}

bool KeyHook::keyInstead( kname skeyname, kname dkeyname )
{
	key skey,dkey;
	skey = GetKeyCode( skeyname );
	if( skey == 0 )
	{   cerr<<"unknow key name:"<<skeyname.c_str()<<endl;
		return false;
	}
	dkey = GetKeyCode( dkeyname );
	if( dkey == 0 )
	{   cerr<<"unknow key name:"<<dkeyname.c_str()<<endl;
		return false;
	}
	cerr<<"skey: "<<skeyname.c_str()<<" "<<skey<<endl;
	cerr<<"dkey: "<<dkeyname.c_str()<<" "<<dkey<<endl;
	return keyInstead( skey, dkey );
}

bool KeyHook::keyFlollow(key fkey, key skey)
{
	key_follow.insert( keyMap::value_type(fkey,skey) );
	return true;
}

bool KeyHook::keyFlollow( kname fkeyname, kname skeyname )
{
	key fkey,skey;
	fkey = GetKeyCode( fkeyname );
	if( fkey == 0 )
	{   cerr<<"unknow key name:"<<fkeyname.c_str()<<endl;
		return false;
	}	
	skey = GetKeyCode( skeyname );
	if( skey == 0 )
	{   cerr<<"unknow key name:"<<skeyname.c_str()<<endl;
		return false;
	}
	return keyFlollow( fkey, skey );	
}

bool KeyHook::keyEventDwn(key hkey,keyProc pf)
{
	key_event_dwn.insert( keyFuncMap::value_type(hkey,pf) );
	return true;
	
	
}

bool KeyHook::keyEventDwn(kname hkname,keyProc pf)
{
	key hkey = GetKeyCode( hkname );
	if( hkey == 0 )
	{
	    cerr<<"unknow key name:"<<hkname.c_str()<<endl;
	    return false;
	}
	key_event_dwn.insert( keyFuncMap::value_type(hkey,pf) );
	return true;
	
	
}

bool KeyHook::keyEventUp(key hkey,keyProc pf)
{
	key_event_up.insert( keyFuncMap::value_type(hkey,pf) );
	return true;
}

bool KeyHook::keyEventUp(kname hkname,keyProc pf)
{
	key hkey = GetKeyCode( hkname );
	if( hkey == 0 )
	{
	    cerr<<"unknow key name:"<<hkname.c_str()<<endl;
	    return false;
	}
	key_event_up.insert( keyFuncMap::value_type( hkey,pf ) );
	return true;
	
	
}

bool KeyHook::mkeyEventAdd(mkey mkey, mkeyProc pf)
{
	mkeyfuncmap.insert( mkeyFuncMap::value_type( mkey, pf ) );
	return true;
}

key KeyHook::GetKeyCode(const kname key_name )
{
	key key_code;
	//a-z A-Z and 0-9 keycode is the sanme as the ascii code
	if( strlen(key_name.c_str()) == 1 )
	{
		//if a-z convert to A-Z
		if( *key_name.c_str() >= 97 &&
		    *key_name.c_str() <= 122 )
		{
		    key_code =  *key_name.c_str()-32;   	
		}
		else 
		{   key_code = *(key_name.c_str());}
	}

	//other keys
	else if( stricmp( key_name.c_str(), "BACK" ) == 0 )
	{    key_code=0x08;}
	else if( stricmp( key_name.c_str(), "TAB" ) == 0 )
	{    key_code=0x09;}
	else if( stricmp( key_name.c_str(), "CLEAR" ) == 0 )
	{    key_code=0x0C;}
	else if( stricmp( key_name.c_str(), "RETURN" ) == 0 )
	{    key_code=0x0D;}
	else if( stricmp( key_name.c_str(), "SHIFT" ) == 0 )
	{    key_code=0x10;}
	else if( stricmp( key_name.c_str(), "CONTROL" ) == 0 )
	{    key_code=0x11;}
	else if( stricmp( key_name.c_str(), "MENU" ) == 0 )
	{    key_code=0x12;}
	else if( stricmp( key_name.c_str(), "PAUSE" ) == 0 )
	{    key_code=0x13;}
	else if( stricmp( key_name.c_str(), "CAPITAL" ) == 0 )
	{    key_code=0x14;}
	else if( stricmp( key_name.c_str(), "SPACE" ) == 0 )
	{    key_code=0x20;}
	else if( stricmp( key_name.c_str(), "PRIOR" ) == 0 )
	{    key_code=0x21;}
	else if( stricmp( key_name.c_str(), "NEXT" ) == 0 )
	{    key_code=0x22;}
	else if( stricmp( key_name.c_str(), "END" ) == 0 )
	{    key_code=0x23;}
	else if( stricmp( key_name.c_str(), "HOME" ) == 0 )
	{    key_code=0x24;}
	else if( stricmp( key_name.c_str(), "LEFT" ) == 0 )
	{    key_code=0x25;}
	else if( stricmp( key_name.c_str(), "UP" ) == 0 )
	{    key_code=0x26;}
	else if( stricmp( key_name.c_str(), "RIGHT" ) == 0 )
	{    key_code=0x27;}
	else if( stricmp( key_name.c_str(), "DOWN" ) == 0 )
	{    key_code=0x28;}
	else if( stricmp( key_name.c_str(), "SELECT" ) == 0 )
	{    key_code=0x29;}
	else if( stricmp( key_name.c_str(), "PRINT" ) == 0 )
	{    key_code=0x2A;}
	else if( stricmp( key_name.c_str(), "EXECUTE" ) == 0 )
	{    key_code=0x2B;}
	else if( stricmp( key_name.c_str(), "SNAPSHOT" ) == 0 )
	{    key_code=0x2C;}
	else if( stricmp( key_name.c_str(), "INSERT" ) == 0 )
	{    key_code=0x2D;}
	else if( stricmp( key_name.c_str(), "DELETE" ) == 0 )
	{    key_code=0x2E;}
	else if( stricmp( key_name.c_str(), "HELP" ) == 0 )
	{    key_code=0x2F;}
	else if( stricmp( key_name.c_str(), "LWIN" ) == 0 )
	{    key_code=0x5B;}
	else if( stricmp( key_name.c_str(), "RWIN" ) == 0 )
	{    key_code=0x5C;}
	else if( stricmp( key_name.c_str(), "APPS" ) == 0 )
	{    key_code=0x5D;}
	else if( stricmp( key_name.c_str(), "SLEEP" ) == 0 )
	{    key_code=0x5F;}
	else if( stricmp( key_name.c_str(), "NUMPAD0" ) == 0 )
	{    key_code=0x60;}
	else if( stricmp( key_name.c_str(), "NUMPAD1" ) == 0 )
	{    key_code=0x61;}
	else if( stricmp( key_name.c_str(), "NUMPAD2" ) == 0 )
	{    key_code=0x62;}
	else if( stricmp( key_name.c_str(), "NUMPAD3" ) == 0 )
	{    key_code=0x63;}
	else if( stricmp( key_name.c_str(), "NUMPAD4" ) == 0 )
	{    key_code=0x64;}
	else if( stricmp( key_name.c_str(), "NUMPAD5" ) == 0 )
	{    key_code=0x65;}
	else if( stricmp( key_name.c_str(), "NUMPAD6" ) == 0 )
	{    key_code=0x66;}
	else if( stricmp( key_name.c_str(), "NUMPAD7" ) == 0 )
	{    key_code=0x67;}
	else if( stricmp( key_name.c_str(), "NUMPAD8" ) == 0 )
	{    key_code=0x68;}
	else if( stricmp( key_name.c_str(), "NUMPAD9" ) == 0 )
	{    key_code=0x69;}
	else if( stricmp( key_name.c_str(), "MULTIPLY" ) == 0 )
	{    key_code=0x6A;}
	else if( stricmp( key_name.c_str(), "ADD" ) == 0 )
	{    key_code=0x6B;}
	else if( stricmp( key_name.c_str(), "SEPARATOR" ) == 0 )
	{    key_code=0x6C;}
	else if( stricmp( key_name.c_str(), "SUBTRACT" ) == 0 )
	{    key_code=0x6D;}
	else if( stricmp( key_name.c_str(), "DECIMAL" ) == 0 )
	{    key_code=0x6E;}
	else if( stricmp( key_name.c_str(), "DIVIDE" ) == 0 )
	{    key_code=0x6F;}
	else if( stricmp( key_name.c_str(), "F1" ) == 0 )
	{    key_code=0x70;}
	else if( stricmp( key_name.c_str(), "F2" ) == 0 )
	{    key_code=0x71;}
	else if( stricmp( key_name.c_str(), "F3" ) == 0 )
	{    key_code=0x72;}
	else if( stricmp( key_name.c_str(), "F4" ) == 0 )
	{    key_code=0x73;}
	else if( stricmp( key_name.c_str(), "F5" ) == 0 )
	{    key_code=0x74;}
	else if( stricmp( key_name.c_str(), "F6" ) == 0 )
	{    key_code=0x75;}
	else if( stricmp( key_name.c_str(), "F7" ) == 0 )
	{    key_code=0x76;}
	else if( stricmp( key_name.c_str(), "F8" ) == 0 )
	{    key_code=0x77;}
	else if( stricmp( key_name.c_str(), "F9" ) == 0 )
	{    key_code=0x78;}
	else if( stricmp( key_name.c_str(), "F10" ) == 0 )
	{    key_code=0x79;}
	else if( stricmp( key_name.c_str(), "F11" ) == 0 )
	{    key_code=0x7A;}
	else if( stricmp( key_name.c_str(), "F12" ) == 0 )
	{    key_code=0x7B;}
	else if( stricmp( key_name.c_str(), "NUMLOCK" ) == 0 )
	{    key_code=0x90;}
	else if( stricmp( key_name.c_str(), "SCROLL" ) == 0 )
	{    key_code=0x91;}
	else if( stricmp( key_name.c_str(), "LSHIFT" ) == 0 )
	{    key_code=0xA0;}
	else if( stricmp( key_name.c_str(), "RSHIFT" ) == 0 )
	{    key_code=0xA1;}
	else if( stricmp( key_name.c_str(), "LCTRL" ) == 0 )
	{    key_code=0xA2;}
	else if( stricmp( key_name.c_str(), "RCTRL" ) == 0 )
	{    key_code=0xA3;}
	else if( stricmp( key_name.c_str(), "LMENU" ) == 0 )
	{    key_code=0xA4;}
	else if( stricmp( key_name.c_str(), "RMENU" ) == 0 )
	{    key_code=0xA5;}
	else
	{    key_code=0x00;}

	return key_code;
}
kname KeyHook::GetKeyName( const key key_code )
{
	kname key_name;
	if( ((key_code>=65) && (key_code <= 90))      //A-Z
		|| ((key_code>=97) && (key_code<=122))    //a-z
		|| ((key_code>=48) && (key_code<=57)) )    //0-9
	{
		// a-z A-Z key name is the same as ascii code
		char tmp[10];
		memset( tmp, 0x00, sizeof(tmp) );
		sprintf( tmp, "%c", key_code );
		string str_tmp(tmp);
		return str_tmp;
	}
	switch( key_code )
	{
	case 0x08:
		key_name = "BACK";
		break;
	case 0x09:
		key_name = "TAB";
		break;
	case 0x0C:
		key_name = "CLEAR";
		break;
	case 0x0D:
		key_name = "RETURN";
		break;
	case 0x10:
		key_name = "SHIFT";
		break;
	case 0x11:
		key_name = "CTRL";
		break;
	case 0x12:
		key_name = "MENU";
		break;
	case 0x13:
		key_name = "PAUSE";
		break;
	case 0x14:
		key_name = "CAPITAL";
		break;
	case 0x20:
		key_name = "SPACE";
		break;
	case 0x21:
		key_name = "PRIOR";
		break;
	case 0x22:
		key_name = "NEXT";
		break;
	case 0x23:
		key_name = "END";
		break;
	case 0x24:
		key_name = "HOME";
		break;
	case 0x25:
		key_name = "LEFT";
		break;
	case 0x26:
		key_name = "UP";
		break;
	case 0x27:
		key_name = "RIGHT";
		break;
	case 0x28:
		key_name = "DOWN";
		break;
	case 0x29:
		key_name = "SELECT";
		break;
	case 0x2A:
		key_name = "PRINT";
		break;
	case 0x2B:
		key_name = "EXECUTE";
		break;
	case 0x2C:
		key_name = "SNAPSHOT";
		break;
	case 0x2D:
		key_name = "INSERT";
		break;
	case 0x2E:
		key_name = "DELETE";
		break;
	case 0x2F:
		key_name = "HELP";
		break;
	case 0x5B:
		key_name = "LWIN";
		break;
	case 0x5C:
		key_name = "RWIN";
		break;
	case 0x5D:
		key_name = "APPS";
		break;
	case 0x5F:
		key_name = "SLEEP";
		break;
	case 0x60:
		key_name = "NUMPAD0";
		break;
	case 0x61:
		key_name = "NUMPAD1";
		break;
	case 0x62:
		key_name = "NUMPAD2";
		break;
	case 0x63:
		key_name = "NUMPAD3";
		break;
	case 0x64:
		key_name = "NUMPAD4";
		break;
	case 0x65:
		key_name = "NUMPAD5";
		break;
	case 0x66:
		key_name = "NUMPAD6";
		break;
	case 0x67:
		key_name = "NUMPAD7";
		break;
	case 0x68:
		key_name = "NUMPAD8";
		break;
	case 0x69:
		key_name = "NUMPAD9";
		break;
	case 0x6A:
		key_name = "MULTIPLY";
		break;
	case 0x6B:
		key_name = "ADD";
		break;
	case 0x6C:
		key_name = "SEPARATOR";
		break;
	case 0x6D:
		key_name = "SUBTRACT";
		break;
	case 0x6E:
		key_name = "DECIMAL";
		break;
	case 0x6F:
		key_name = "DIVIDE";
		break;
	case 0x70:
		key_name = "F1";
		break;
	case 0x71:
		key_name = "F2";
		break;
	case 0x72:
		key_name = "F3";
		break;
	case 0x73:
		key_name = "F4";
		break;
	case 0x74:
		key_name = "F5";
		break;
	case 0x75:
		key_name = "F6";
		break;
	case 0x76:
		key_name = "F7";
		break;
	case 0x77:
		key_name = "F8";
		break;
	case 0x78:
		key_name = "F9";
		break;
	case 0x79:
		key_name = "F10";
		break;
	case 0x7A:
		key_name = "F11";
		break;
	case 0x7B:
		key_name = "F12";
		break;
	case 0x90:
		key_name = "NUMLOCK";
		break;
	case 0x91:
		key_name = "SCROLL";
		break;
	case 0xA0:
		key_name = "LSHIFT";
		break;
	case 0xA1:
		key_name = "RSHIFT";
		break;
	case 0xA2:
		key_name = "LCTRL";
		break;
	case 0xA3:
		key_name = "RCTRL";
		break;
	case 0xA4:
		key_name = "LMENU";
		break;
	case 0xA5:
		key_name = "RMENU";
		break;
	default:
		key_name = "unknown key";
		break;
	}
	return key_name;
}

bool KeyHook::DoHook()
{
	//add condition to turn/off hook
	return true;
}
bool KeyHook::ChkMkey( mkey keys)
{
	mkey::iterator it_mkey;
	bool ret=true;
	for( it_mkey = keys.begin();
	     it_mkey != keys.end();
	     it_mkey++ )
	{
		key tmp = GetKeyCode( *it_mkey );
	    short keystat =	GetKeyState( tmp );
		if( (keystat&0x0080) != 0x0080 )
		{
			//cout<<*it_mkey<<" "<<tmp<<" "<<hex<<keystat<<endl;
			ret = false;
		}
	}
	
	return ret;
}

KeyHook::~KeyHook()
{
}
